home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
System
/
Dragonsmith 1.1.1
/
Documentation
/
Dragonsmith Manual (text)
< prev
next >
Wrap
Text File
|
1994-01-15
|
84KB
|
2,312 lines
------------------------------------------------------
Dragonsmith Programmer’s Manual (text format)
------------------------------------------------------
version 1.1
Sunday, October 11, 1992
by Paul Hoffman
“Dragons do not enter into this story…”
Send bug reports, comments, and suggestions to:
Mail Paul Hoffman
302 W. Davis, Apt. 2
Ann Arbor MI 48103
U.S.A.
Internet paul.hoffman@umich.edu
dragonsmith@umich.edu
AFS /afs/umich.edu/user/n/k/nkuitse
**************************************************
* NOTE: *
* *
* This document is a text-only version of the *
* file “Dragonsmith Programmer’s Manual” *
* *
* The text is all the same, but a few tables *
* and figures are missing from this file *
* *
**************************************************
------------------------------------------------------
CONTENTS
------------------------------------------------------
Introduction
What is Dragonsmith?
What is drag-and-drop, and what is it good for?
How does the Finder know what can be drag-and-dropped where?
What is a dragon, and why is it called that?
What can Dragonsmith do, and what can’t it do?
Can I use Dragonsmith with System 6?
Why use object-oriented programming (OOP)?
Where can I find out more about OOP?
Does Dragonsmith use the THINK Class Library?
How do I create my own dragon?
How do I upgrade from Dragonsmith v1.0b2 to v1.1?
Will I have to rewrite my code when the next release of Dragonsmith comes out?
Getting started
Dragonsmith 1.1 files
Upgrading from Dragonsmith 1.0b2
PreDragon
Creating the project file
Creating the project resource file
Writing a subclass of Dragon
Compiling and debugging your dragon
Program flow
When something goes wrong…
Error ‘Can’t open file “PreDragon”’
Syntax error at the first line
Link error ‘Undefined: CreateGDragon’
AEProcessAppleEvent returns error –608 (noOutstandingHLE)
AEProcessAppleEvent returns error –609 (connectionInvalid)
Your dragon won’t drop (or it ‘overdrops’)
Your dragon crashes shortly after an Apple event is handled
Notes on Apple events
How to…
Automatic disk and folder opening
Adding a menu
Adding a preference setting
Dragons ahoy!
Dragon instance variables
Dragon methods
Resources
------------------------------------------------------
INTRODUCTION
------------------------------------------------------
What is Dragonsmith?
Dragonsmith is an object-oriented framework for developing drag-and-drop
applications under Macintosh System 7.
What is drag-and-drop, and what is it good for?
The drag-and-drop feature of the System 7 Finder lets you open files using
an application other than the one that created it. Here’s a (rather
simplistic) outline of how this works:
• The user drags a file’s icon onto that of a drag-and-drop application
• The Finder launches the application (if it wasn’t already running)
• The Finder sends the application an Apple event telling it to open the
file
• The application opens the file
How does the Finder know what can be drag-and-dropped where?
As you drag a file’s icon around the desktop, the Finder hilites an
application icon the pointer passes over only if the application is capable
of opening that type of file. (The situation is more complicated when you
drag more than one icon at a time.) The Finder uses the desktop database to
decide which applications can open which types of files; this information in
turn comes from ‘FREF’ resources found in the application files. Because
the desktop database sometimes gets messed up (and out of a general sense of
caution), applications developed with Dragonsmith do their own checking of
‘FREF’ resources and file types.
What is a dragon, and why is it called that?
A dragon is an application that is driven primarily by drag-and-drop — it
doesn’t do much if you double-click on its icon, but drop a few files on it
and watch it go! Instead of ‘opening’ drag-and-dropped files, dragons
‘process’ them. This ‘processing’ can be any type of action — deleting a
file, compiling an index of it, extracting sounds from its resource fork,
etc. Here’s a list of some dragon-and-drop applications and what they do:
Alias Director 2.7 Creates aliases to files, folders, and disks.
Alias Finder Opens the Finder window containing the original file or
folder of an alias.
FileTyper 3.0 Allows one to change the file type, creator, and other
attributes of files.
GroupInfo Displays the total amount of disk space used by a group
of files and folders.
Paradigma 2.0 Finds and replaces strings in a text file.
Save a BNDL Refreshes a file’s BNDL information to update its icons,
etc. in the Finder.
StuffIt Expander™ Expands StuffIt and CompactPro archives and AppleLink
packages.
Text Merger 1.02 Merges several text files into a single file.
There is a great deal of variety among these applications (and others like
them). Some of them will accept drag-and-dropped folders or disks in
addition to files. Some process files quietly and quit automatically when
they’re done; others stick around to give you the opportunity to change
settings. Some only run in System 7; others will run in System 6, using the
Standard File routines to choose the files you want to process. The
important thing is what they all have in common — drag-and-drop ‘batch’
processing of files.
Why the name dragon?
• Dragons are drag-and-dropped on (of course).
• Dragons are distant cousins of UNIX daemons (which, strangely enough, do
their thing in the wings).
• Dragons have been known to enter (quite uninvited) into the stories of a
certain amateur writer.
What can Dragonsmith do, and what can’t it do?
Dragonsmith can be used to create any application, but it wouldn’t make
sense to develop one that didn’t feature drag-and-drop prominently. There
wouldn’t be much point, for example, in using Dragonsmith to make a word
processor — it would be more suited for an application that displays the
total number of words and paragraphs in a group of text files dropped on it.
Because most dragons will probably perform simple tasks, Dragonsmith has
been designed with simplicity in mind. A simple Dragonsmith-produced
application typically requires only about 20–25K of disk space and runs in
40–50K of memory. Because of the object-oriented nature of the Dragonsmith
kit, however, advanced features can easily be added to a dragon.
On the other hand, dragons developed with Dragonsmith have several more or
less advanced capabilities:
Preferences
Automatic resource-based preferences file management is built
into all dragons. Any dragon that you create can store, alter, and retrieve
any number of preferences. The user can ask a dragon to use any particular
preferences file that belongs to it by double-clicking on that file in the
Finder. As long as you’re careful to program defensively, the dragon will
still run normally if a preferences file could not be found or created.
Folder searching
You can specify that folders and disks will automatically
be opened recursively to any desired depth in search of files to be
processed.
Alias resolution
If folder searching is enabled, then any alias files
encountered may (optionally) be automatically resolved.
Safe file handling
A dragon will “see” only those documents (files, folders,
and volumes) that the Finder would allow the user to drag-and-drop on it.
If the dragon looks inside a folder dropped on it, or if it receives an Open
Documents event from a source other than the Finder, it will ignore any
files it doesn’t know what to do with.
Smart file filtering
A dragon can easily be made to process only files that
match specific criteria besides just the file type — name, size, creator,
location, etc.
Background processing
A dragon will run quietly in the background if the
user decides to switch to (or launch) another application after dropping
files on it; the dragon will take as much or as little CPU time away from
other applications as you want it to.
Apple event queuing
The user can drag-and-drop documents on a dragon (or
another Apple-event-savvy application can send the dragon an Open Documents
event) while the dragon is busy at work on something else — each batch of
documents will be processed in the order in which it was received.
Auto-quit
You can specify that a dragon will automatically quit after
processing drag-and-dropped documents (or if double-clicked in the Finder).
Environment checking
A dragon will terminate gracefully if launched in a
run-time environment insufficient for its needs — and the specifications for
its run-time requirements can be changed without any source code
modifications.
The following features are not supported by Dragonsmith 1.1 but are under
consideration for future releases:
Apple Event objects
Adding Apple Event Object Model and scripting support is
a high priority. This will enable control of dragons by Apple event-savvy
applications, including scripting environments like UserLand Frontier and
the forthcoming AppleScript. A small Dragon suite of custom Apple events is
a possibility.
Improved error handing
The Dragon class’s error handling capability is
currently rudimentary. Improvements will include better reporting of errors
to the user and, when Apple Event Object support is added, to client
applications.
Progress window
Also in the works is a movable modal dialog to show what
documents are being processed, display error messages, etc.
More sophisticated preferences handling
There is currently no support in the Dragon and Preferences classes for
preferences file ‘Open,’ ‘Save’ and ‘Save As…’ operations. These
capabilities should form part of a general preferences-editing interface —
perhaps via a ‘Preferences…’ item added to the ‘Edit’ menu.
Drakelings
There is a growing trend in the Macintosh world toward
‘miniature’ custom stand-alone drag-and-drop applications created by a
‘master’ application — Frontier’s applets belong to this category, as do the
‘ParaDoids’ created by Espen Aarseth’s excellent Paradigma, and the
‘AutoTypers’ in Daniel Azuma’s FileTyper package. Implementing this type of
document-application hybrid in Dragonsmith is a high priority.
Faceless dragons
Some dragons don’t need any user interface. The Dragon
class currently uses an instance variable autoQuit to designate that a
dragon will quit immediately after receiving the first Open Documents event
it receives. A better solution, however, would be a simpler class (of which
Dragon would become a subclass) designed for creating background-only
dragons. This could significantly reduce the size of simple faceless
dragons, which is especially important in light of the interface
enhancements planned for the Dragon class.
FilterTop support
The freeware FilterTop project currently under development
will provide a programming framework similar in some ways to that of
Dragonsmith. FilterTop is likely to garner a good deal of popularity, and
Dragonsmith and it should be able to complement each other very nicely.
Can I use Dragonsmith with System 6?
No, System 7 is required. The whole point of dragons is defeated in a
no-drag-and-drop, no-Apple-events environment.
Why use object-oriented programming (OOP)?
The center of Dragonsmith’s drag-and-drop framework is provided the Dragon
class. To write your own dragon, you’ll create a new subclass of Dragon
that ‘inherits’ the bulk of its code. Using OOP, you can easily override or
extend Dragon’s behavior without altering any source files — you can change
as much or as little as you need to.
Where can I find out more about OOP?
You might begin by reading parts of the THINK C Object-Oriented Programming
Manual (for THINK C 5.0). Pages 19–24 and 28–51 are a good starting point,
and pages 81–86 contain some information specific to using OOP in THINK C.
Does Dragonsmith use the THINK Class Library?
No. For most drag-and-drop applications, the TCL is overkill — it contains
a vast amount of code that most dragons will never need.
On the other hand, you could use the TCL — using code from Dragon.c, write a
subclass of CApplication called CDragonApp or some such. It shouldn’t be
overwhelmingly difficult.
[Remember, the source code in Dragonsmith may be freely altered and used for
any purpose, subject only to the restrictions mentioned at the beginning of
each file. Applications developed using Dragonsmith may be sold or given
away without restriction.]
How do I create my own dragon?
The easiest way is to base your dragon on one of the template classes
provided. Each of these files has step-by-step instructions on how to use
it to write your own dragon:
PlainTmpl.c A very simple ‘auto-quit’ dragon without any interface
MenusTmpl.c A simple dragon with an added menu
PrefsTmpl.c A simple dragon with an added menu and preferences settings
You might also want to read the source code files for the finished dragons
provided with this release of Dragonsmith — DFileCleaver.c, DFilePaths.c,
and DPasser.c
How do I upgrade from Dragonsmith v1.0b2 to v1.1?
Follow the instructions in the Getting Started section below. The basic
steps are:
• Replace the old Dragonsmith.π with the one in this release. You can
throw away Dragonsmith.debug.π — it’s no longer needed.
• Move the bulk of your ProcessDroppings method to ProcessFile (and
possibly move some to the BeginProcessing and EndProcessing methods) — this
may require adding a few instance variables. Your dragon will no longer
need to directly access the data in the Open Documents event.
• If your dragon added any menus, rewrite its SetUpMenus and DoMenu methods
and remove its basic Apple, File, and Edit menu functionality — they’re now
implemented in Dragon.
• Change your dragon’s signature from ‘Drgn’ to some other unique value, if
you hadn’t already.
• Add some resources to your project’s resource file.
• Recompile all the files in your project.
Will I have to rewrite my code when the next release of Dragonsmith comes
out?
Hopefully not. This manual will warn you about certain features of the
Dragon class that, for future compatibility, are best left alone. Still,
the most important thing is to get the most out of the functionality already
in the Dragon class — future incompatibility dangers shouldn’t inhibit you
more than a little.
------------------------------------------------------
GETTING STARTED
------------------------------------------------------
Dragonsmith 1.1 files
Dragonsmith
! Read me
Base files <Normally, you shouldn’t add to this folder or alter the files
in it — if you use a copy of one of these files, move it up into
the Dragonsmith folder>
Classes
AppleEventQueue.c
AppleEventQueue.h
Preferences.c <A resource-based preferences management class that
may be used outside of Dragonsmith>
Preferences.h
DFileCleaver.c
DFilePaths.c
DPasser.c
Dragon.c <Code for the Dragon class>
Dragon.h
Dragonsmith TMPLs <ResEdit templates for editing custom resource types used
in Dragonsmith>
Dragonsmith.π <Use a copy of this project file with your code added for
greater debugging control than is provided by (a copy of) Template.π>
FileCleaver.π
FileCleaver.π.rsrc
FilePaths.π
FilePaths.π.rsrc
Main.c <Contains the function main used for all dragons>
Passer.π
Passer.π.rsrc
PreDragon.c <Source for the PreDragon precompiled headers file used by
Dragonsmith projects>
Utilities <Source code containing various utility functions>
[numerous files]
Documentation
! Release notes
Dragonsmith Programmer’s Manual
Dragonsmith Manual (text) <This file>
Preferences class notes
Dragons
! Read Me (Dragons)
File Cleaver 1.1
File Paths 1.1 <Used to produce this list of files>
Passer 1.1 <A dragon that passes Open Documents events to high level
event-aware processes — vital for debugging Apple event-handling code in the
THINK C Debugger>
Template files <Source code and resource template files for three simple
kinds of Dragon subclass>
DMenusTmpl.c
DPlainTmpl.c
DPrefsTmpl.c
MenusTmpl.π.rsrc
PlainTmpl.π.rsrc
PrefsTmpl.π.rsrc
Template.π <Project file to copy and use with the template files>
Upgrading from Dragonsmith 1.0b2
The original release of Dragonsmith was much more simplistic. Processing of
documents was treated as a single step — the dragon developer was required
to write (in ProcessDroppings) a loop to extract, then process, each
document from the Open Documents Event. Any desired filtering of documents
(treating files and folders differently, resolving alias files, opening only
files of a certain type, etc.) had to be done by the subclass of Dragon.
Programmers who needed file information were responsible for calling
PBGetCatInfo themselves. In Dragonsmith 1.1, most of this ‘grunt’ work is
done for you by the Dragon class.
The changes in functionality have brought about several significant changes
in the Dragon class iinterface, and all Dragon 1.0b2 subclasses will have to
be modified to work with Dragon 1.1.
ProcessDroppings
In Dragonsmith 1.0b2, every Dragon subclass had to override
ProcessDroppings. In version 1.1, that’s no longer true. In general,
ProcessFile (and possibly ProcessDirectory) should be overridden;
ProcessDroppings should not.
To update your dragon to Dragonsmith 1.1, you’ll probably move most of the
code from your ProcessDroppings method to ProcessFile — though some of it
may need to go into BeginProcessing and EndProcessing. Updating your dragon
will probably also require changing a few local variables in your old
ProcessDroppings method to instance variables.
In addition, ProcessDroppings’ parameters have changed. The docs parameter
isn’t needed any more because information about the current file or
directory is always accessible in *curDocFSS (an FSSpec record) and
*curDocPB (a multi-purpose parameter block subsuming the HParamBlockRec,
CInfoPBRec, and other low-level File Manager param-blocks). The numDocs
parameter has no counterpart in Dragon 1.1 — a more sophisticated file
counting and tracking mechanism is planned for a future version of
Dragonsmith.
Menus
The new Dragon class implements standard Apple and Edit menus, plus a File
menu with a single Quit item. The DFilePaths class released as part of
Dragonsmith 1.0b2 used a different means to implement essentially the same
menus — any dragons modeled after it will need to be changed to adhere to
Dragon 1.1’s menu scheme. Look at the DMenusTmpl.c file for an example of
how a dragon should add menus.
Signatures and the Desktop Manager
Dragonsmith 1.0b2 didn’t make it clear that every new dragon should have a
unique application signature (and, for clarity, a distinct icon in the
Finder). If more than one application in your Mac work environment has the
same signature, then drag-and-drop for either of them (or both) may not work
properly. You are encouraged to assign your dragons unique signatures and
register each one with Apple.
PreDragon
One of the first things you should do is to make a precompiled headers file
from PreDragon.c. The resulting file should be named PreDragon and placed
in the Base Files folder. The source files in the Dragonsmith kit all rely
on these precompiled headers.
Creating the project file
Start with a copy of Template.π (or a copy of Dragonsmith.π if you want more
control over debugging), then add the file which holds the source code for
your dragon. The code for a simple dragon will most likely fit into a
single segment, not counting extra segments (such as ‘CODE’ 0) that THINK C
creates transparently.
Make sure you choose ‘Set Project Type…’ from THINK C’s Project menus and
set the appropriate values for Creator (signature), partition, and SIZE
flags.
Creating the project resource file
In general, you should start with a copy of one of the …Tmpl.π.rsrc files
and make modifications as necessary. The first changes you make should be
in ‘STR ’ 128, the ‘FREF’ resources, and ‘DrPr’ 128. The Resources section
in this document and the source files in the ‘Template Files’ folder contain
descriptions of the various resources used by Dragonsmith, as well as
information on which resources to change and how.
Writing a subclass of Dragon
It’s recommended that you start with one of the template classes provided in
the ‘Template Files’ folder. Information on how to make a dragon this way
is provided in the source files. If you had previously developed a dragon
using Dragonsmith 1.0b2, you should read the section Upgrading from
Dragonsmith 1.0b2 above. For tips on adding specific functionality to your
dragon, refer to the section How To… below.
Constructor method
Your dragon should use a constructor (a method with no return-value, whose
name is that of the class to which it belongs) to initialize any instance
variables it adds. Your dragon’s constructor will be executed (immediately
after the Dragon class’s constructor) when the gDragon object is made in
CreateGDragon. It should set any default instance variable values that you
want to be different from the values assigned in Dragon::Dragon. If your
dragon implements any custom preferences resources, you should add instance
variables to hold their values and your constructor should set defaults for
them (in case the ReadPrefs method fails).
ProcessFile and ProcessDirectory
These are the two most important methods — without overriding one or both of
them, your dragon won’t do anything. If your dragon makes simple changes to
a file or directory’s catalog information, it can use the macros described
below (curFileType, curDocCreated, etc.) to change the appropriate fields in
curDocPB, then (at the end of ProcessFile or ProcessDirectory) call the
SaveDocInfo method to have the information changed on disk. For more
information, see Macros below and read the notes for SaveDocInfo in the
Methods section.
CreateGDragon
This function is not defined in Dragon.c — the code for it must be found in
one of your dragon’s source files. It performs the very important task of
creating a dragon object of the desired Dragon subclass, and can be written
as simply as this:
Dragon *CreateGDragon (void)
{
return (Dragon *) new DMyDragonClass;
}
Compiling and debugging your dragon
Passer
Drag-and-drop is at the heart of dragons, so you’ll need a way to debug your
dragon’s ProcessFile method (and related code). Unfortunately, there’s no
way to drag-and-drop files onto your dragon while it’s running in the
THINK C Debugger. Instead, you’ll have to use the ‘Passer’ dragon provided
with Dragonsmith 1.1 to transmit Open Documents events to your dragon while
it’s being debugged. To do so, choose ‘Run’ from the Project menu in
THINK C, then start up Passer and choose ‘Target…’ from its Options menu.
Once you’ve selected your dragon’s process from the resulting PPCBrowser
dialog, any Open Documents events sent to Passer will simply be rerouted to
your dragon without modification. Set a breakpoint in ProcessFile (or
ProcessDirectory) to ‘catch’ the event, then debug from there.
Apple events and the THINK C Debugger
While running your dragon in the THINK C Debugger, you must be careful not
to set any breakpoints in code executed between a call to WaitNextEvent that
returns a high-level event and the call to AEProcessAppleEvent that
dispatches the event to the appropriate Apple event handler. You can set
breakpoints in the handler itself and in the Dragon method it calls. If you
forget this and set a breakpoint in the wrong place, AEProcessAppleEvent
will return a noOutstandingHLE (–608) error and the event will not be
dispatched. Presumably this is due to a time-out internal to the Apple
Event Maneger package (‘PACK’ 8), because it seems to happen whether the
source of the event specifies a time-out value or not.
If you want to send yourself an Apple event, use the kCurrentProcess
constant instead of sending by signature — your signature under the THINK C
Debugger is ‘KAHL’ and THINK C or the THINK C Debugger may intercept the
event.
Don’t set any breakpoints between a call to WaitNextEvent that returns a
high-level event and the ensuing call to AEProcessAppleEvent. Otherwise,
the Apple event will time out and AEProcessAppleEvent will return –608
(noOutstandingHLE). This may also be true of AcceptHighLevelEvent — it
hasn’t been tested.
------------------------------------------------------
PROGRAM FLOW
------------------------------------------------------
Figure 1 shows the flow of execution of a dragon as it starts up and
subsequently receives an Open Documents event:
<Figure 1 omitted>
The main event loop in Dragon is essentially the same as that in almost any
Macintosh application. For each event returned by WaitNextEvent, the
DoEvent method calls the appropriate event-handling method — DoMouseDown,
DoSuspend, DoResume, DoKeyDown, etc. When an Open Documents event is
received by your dragon, DoEvent calls DoHighLevelEvent, which dispatches
the event to HandleOdoc, the Open Documents handler installed in the
InitAppleEvents method. HandleOdoc in turn sends a DoOdoc message to
gDragon, the global dragon object. DoOdoc extracts the event’s
keyDirectObject parameter (the list of documents to open) and uses it as a
parameter to ProcessDroppings, which calls ProcessDoc for each document in
the list.
Things can get considerably more complicated than this diagram shows:
• When dirDepthLimit < 0 and ProcessDoc encounters a directory,
ProcessDocsInDirectory will be executed instead of ProcessFile or
ProcessDirectory, and recursion back to ProcessDoc will probably occur.
• The ProcessOwnedFile method will be called instead if your dragon is
asked to open a file that belongs to it (i.e., one whose creator code is
your dragon’s signature). Typically, this is a preferences file, but a
Dragon subclass might use this feature for other things — to implement
script files, for example.
• If a document your dragon is asked to open is of a type that it doesn’t
‘know about’ (i.e., that is not covered by its ‘FREF’ resources), or if the
document is rejected by CustomFilterDoc, then ProcessDoc will not be
executed.
• If the dragon is busy processing something when an Open Documents event
is received, DoOdoc will call SuspendAEvent instead of ProcessDroppings.
This will add the event to a queue for later processing — the event will be
‘on hold’ until all preceding ‘odoc’ events have been processed. The next
time WaitNextEvent fails to return an event, Run will call ResumeAEvent to
complete handling of the event. This way, all Open Documents events will be
processed in the order in which they were received. Note that when a Quit
Application event is received, any suspended events will return an error to
the sending process and will not result in any documents being processed.
------------------------------------------------------
WHEN SOMETHING GOES WRONG…
------------------------------------------------------
Error ‘Can’t open file “PreDragon”’
Either you haven’t created the PreDragon precompiled headers file from
PreDragon.c, or it’s located where THINK C can’t find it. See Creating your
dragon skeleton above for more information.
Syntax error at the first line
The problem may be with your project prefix — it should end with the line
#include "PreDragon"
Choose ‘Options…’ from THINK C’s Edit menu to bring up a dialog where you
can set this. You should not #include <MacHeaders> in the project prefix.
Link error ‘Undefined: CreateGDragon’
You’ve forgotten to define (write the code for) the CreateGDragon function.
It should create an instance of your Dragon subclass (not of Dragon) and
return a reference to the object, like this:
Dragon *CreateGDragon (void)
{
return (Dragon *) new DMyDragonClass;
}
AEProcessAppleEvent returns error –608 (noOutstandingHLE)
You may have set a breakpoint between the WaitNextEvent call that obtained
the event and the AEProcessAppleEvent call that dispatched to your handler —
see Compiling and debugging your dragon above for more information on using
the THINK C Debugger.
It is safe to set a breakpoint in your Apple event handler, or in the code
that it calls (DoOapp, DoOdoc, DoPdoc, or DoQuit).
AEProcessAppleEvent returns error –609 (connectionInvalid)
You may have forgotten to set the ‘high level event aware’ flag in your
project’s ‘SIZE’ resource. Both the sender and receiver of a high-level
event must have this flag set.
Your dragon won’t drop (or it ‘overdrops’)
If this is the first time you’ve built your dragon, check to make sure that
you’ve given it a unique signature and not ‘????’ (or ‘Drgn’ — Dragonsmith
v1.0b2 implied that you should use this value).
If you’ve built it before, then there may be a problem with the desktop
database. Delete any obsolete versions of your dragon that accepted
different types (or move them to a floppy disk) and rebuild the desktop.
(The utility application ‘Save a BNDL’ may let you avoid this step — but use
it with caution.)
Your dragon crashes shortly after an Apple event is handled
Your Apple event may be returning an invalid error code. Make sure it
returns an error listed in Inside Macintosh VI. It’s not clear why this
would cause a bus error, but it seemed to do so in an early version of
Dragonsmith.
------------------------------------------------------
NOTES ON APPLE EVENTS
------------------------------------------------------
Any application which is high level event aware will receive exactly one of
the three following kinds of events when launched by the Finder:
Open Application
If this event is received, the user double-clicked on the
application in the Finder, or selected the application and chose ‘Open’ from
the File menu.
Open Documents
If this event is received, the user double-clicked on a file
(or group of files) belonging to the application, or selected the file(s)
and chose ‘Open’ from the File menu, or drag-and-dropped it (or them) onto
the application’s icon.
Print Documents
If this event is received, the user selected a file (or
group of files) and chose ‘Print’ from the File menu.
An application may receive any number of Open Documents and Print Documents
from the Finder after it has started.
Other processes may launch an application with any kind of Apple event, or
none at all, and may later send any kind and number of Apple events to it.
Because of this, your code should never rely on receiving any Apple events
in any sequence.
QuitApplication
The Finder will send a Quit Application event to an
application if the user chooses ‘Restart’ or ‘Shut Down’ from the ‘Special’
menu (and possibly under other circumstances as well). Other processes may
send this event to the application at any time.
------------------------------------------------------
HOW TO…
------------------------------------------------------
Automatic disk and folder opening
To make your dragon automatically process files inside directories (i.e.,
volumes and folders), change the value of dirDepthLimit (in your constructor
as well as in the ‘DrPr’ 128 resource) to a negative number indicating how
many levels down your dragon will look. This feature can be very handy, but
it can also be very dangerous. To avoid stack overflow, you should not set
dirDepthLimit to a value less than about -100. If you set resolveAliases =
TRUE, then the possibility of recursion becomes an issue.
Figure 2 shows a hierarchical view of the contents of a folder
‘Miscellaneous’ — the depth of each file or folder (except 0 Another File
alias) is indicated by the number its name begins with. How your dragon
should set its instance variables (and the corresponding values in its
‘DrPr’ 129 resource) depends on what action you want it to take when the
user drag-and-drops the hilited files on it. (For the sake of this example,
it’s assumed that your dragon has ‘FREF’ resources for types ‘fold’ and
‘TEXT’ and that all the files pictured are text files.)
<Figure 2 omitted>
Table 1 gives details on which combinations of values will result in a
particular set of documents being processed. In several cases, the value of
one or two instance variables is irrelevant.
<Table 1 omitted>
Adding a menu
Refer to the file ‘MenusTmpl.c’ for a simple illustration of how to add
menus to your dragon. The information below on adding a preferences setting
also shows how to add a menu.
Adding a preference setting
The ‘PrefsTmpl.c’ file contains sample code showing you how to implement
preferences in Dragonsmith. By default, every dragon will use a preferences
file, whether it needs it or not — to prevent your dragon from finding or
creating a preferences file, put a null string in the ‘STR ’ 128 resource
and remove the ‘FREF’ resource for the type designated by the instance
variable prefsFileType.
The ‘PrRo’ 128 resource in your dragon’s preferences (and application) file
contains references to the preferences resources which belong to it. The
first resource designated in the ‘PrRo’ 128 resource must be ‘DrPr’ 128,
which contains several important values used by every Dragon subclass (see
below).
Documentation for the Preferences class is minimal in this release of
Dragonsmith — a later release will correct that. In the mean time, reading
the source code for the class may be instructive.
The steps involved in adding a new preference to your dragon are as follows:
• Add a resource in your project resource file to hold the preferences
value(s)
• Create an entry for the added preferences resource at the end of the
‘PrRo’ 128 resource in your project resource file
• Add an instance variable to hold a handle to the added resource
• Add instance variables to your Dragon subclass to hold the values from
your added resource
• In your dragon’s constructor method, initialize the preferences instance
variables to safe, if-all-else-fails default values — your dragon should not
crash or abort if some preferences couldn’t be found
• Override ReadPrefs as indicated below
• Add some way in your dragon’s interface to change the additional
preference(s)
• Add code to change the preferences resource in memory and save it to disk
As an example, take the case of a dragon (named ‘Scruncher’) that uses one
of three compression methods (Fast, Normal, or Tight) to compact files. The
user can choose any one of the three, and may also specify whether Finder
information about the file is preserved in the resulting archive. These
preferences are stored in a ‘ScPr’ 128 resource in the Scruncher.π.rsrc
file. Figure 3 shows the dragon’s added menu:
<Figure 3 omitted>
The DScruncher.c file would look something like this:
#include "Dragon.h"
#include “MenuUtils.h”
#include “Preferences.h”
#define prefScruncherPrefs prefDragonPrefs + 1
enum {
mOptions = mEdit + 1
};
enum {
iFastScrunching,
iNormalScrunching,
iSmallScrunching,
iLine3,
iSaveFInfo
};
typedef struct {
char scrunchType;
Boolean saveFInfo;
} ScrunchOpts;
class DScruncher : public Dragon {
protected:
ScrunchOpts **scrunchOptions;
char scrunchType;
Boolean saveFInfo;
public:
DScruncher (void); // Constructor
protected:
[Protected method declarations omitted for brevity]
};
Dragon *CreateGDragon (void)
{
return (Dragon *) new DScruncher;
}
DScruncher::DScruncher (void)
{
optionsMenu = NULL;
scrunchOptions = NULL;
scrunchType = iNormalScrunching;
saveFInfo = TRUE;
}
void DScruncher::ReadPrefs (void)
{
inherited::ReadPrefs ();
scrunchOptions = (ScrunchOpts **) preferences->GetPrefResource
(prefFilePathOptions);
if (scrunchOptions != NULL) {
HNoPurge ((Handle) scrunchOptions);
scrunchType = scrunchOptions->scrunchType;
saveFInfo = scrunchOptions->saveFInfo;
}
}
void DScruncher::SetUpMenus (void)
{
inherited::SetUpMenus (); // Add the Apple, File, and Edit menus
optionsMenu = GetMenu (mOptions);
InitOptionsMenu ();
InsertMenu (optionsMenu, 0);
DrawMenuBar ();
}
void DScruncher::InitOptionsMenu (void)
{
// Initialize options menu (and set the options while we’re at it)
CheckItem (optionsMenu, iSaveFInfo, saveFInfo);
SetScrunchType (iFastScrunching + scrunchType - 1);
}
void DScruncher::DoMenu (long menuItemCode)
{
if (menuItemCode >> 16 == mOptions)
DoOptionsMenu (menuItemCode & 0xFFFF);
else
inherited::DoMenu (menuItemCode);
}
void DScruncher::DoOptionsMenu (short itemNum)
{
if (itemNum == iSaveFInfo)
ToggleSaveFInfo ();
else
SetScrunchType (itemNum);
// Save any changes that might have been made
preferences->SavePrefResource (prefFilePathOptions);
}
void DScruncher::ToggleSaveFInfo (void)
{
saveFInfo = ToggleMenuItem (optionsMenu, iSaveFInfo);
if (scrunchOptions)
(*scrunchOptions)->saveFInfo = saveFInfo;
}
void DScruncher::SetScrunchType (short scrunchItem)
{
CheckOne (optionsMenu, iFastScrunching, iSmallScrunching, scrunchItem);
scrunchType = scrunchItem;
if (scrunchOptions)
(*scrunchOptions)->scrunchType = scrunchType;
}
void DFilePaths::AdjustMenusBusy (void)
{
inherited::AdjustMenusBusy ();
DisableItem (optionsMenu, 0); // Disable the Options menu
}
void DFilePaths::AdjustMenusIdle (void)
{
inherited::AdjustMenusIdle ();
EnableItem (optionsMenu, 0); // Enable the options menu
}
------------------------------------------------------
DRAGONS AHOY!
------------------------------------------------------
Here’s a list of some dragons that have been spotted by the author on the
(albeit distant) horizon:
Drawk
‘Load’ an awk program, then drag-and-drop input files.
D. D. Rex
Specify text substitutions using grep syntax, then drag-and-drop
files to make the changes in — something like Paradigma, but with grep
capability.
Proto Drake
Make function and method prototype files from drag-and-dropped C
source files.
Icon Elephant
Tired of the Finder spontaneously rearranging your windows’
icons under mysterious circumstances, after you spent five minutes getting
the icons just right? Drag-and-drop a folder on Icon Elephant to restore
‘remembered’ icon positions on demand.
Dragon Deity
Create Dragonsmith source code and related files quickly and
easily; check projects and their resource files for consistency,
completeness, and general good health.
THINK C Thief
Drag-and-drop THINK C 5.0 projects to discover what files they
contain, and more.
------------------------------------------------------
DRAGON INSTANCE VARIABLES
------------------------------------------------------
Most of the instance variables described below should normally not be
altered by subclasses of Dragon. Instance variables whose values are set in
ReadPrefs (from the ‘DrPr’ 128 resource) are noted.
abortProcessing
Boolean abortProcessing;
Don’t change the value of this instance variable directly — call
StopProcessing instead
This flag will be set to TRUE if a serious error prevents the current Open
Documents from continuing, or if the user cancels processing by pressing
Command–period.
acceptableTypes
TypeListHndl acceptableTypes;
Don’t change the value of this instance variable or the data it points to
This is a handle to a list of document types (taken from your dragon’s
‘FREF’ resources) that your dragon knows how to process.
aeQueue
AppleEventQueue *aeQueue;
Don’t change the value of this instance variable
This is a reference to an instance of class AppleEventQueue, which is used
to manage a simple FIFO queue of Open Documents events received while your
dragon is processing documents from a previous event.
appFile
FSSpec appFile;
Don’t change the value of this instance variable
This contains an FSSpec to your application — it’s set in the Dragon class’s
constructor and can be used in any way you want.
appleMenu
MenuHandle appleMenu;
Don’t change the value of this instance variable
This is a handle to the standard Apple menu implemented by the Dragon class.
appResFork
short appResFork;
Don’t change the value of this instance variable
This instance variable contains a refNum to the resource fork of your dragon
— it’s the access path created by the system when the application is
launched.
Note: When running in the THINK C Debugger, appResFork is actually a refNum
to the project resource file. The effect in either case is identical.
autoQuit
Boolean autoQuit;
Will be set in ReadPrefs
This instance variable determines whether your dragon will terminate after
processing the first Apple event it receives — DoOapp, DoOdoc, and DoPdoc
all test the value in autoQuit. Also, because an auto-quit dragon won’t
have any use for menus, the Start method will call SetUpMenus only if
autoQuit != FALSE.
busyCursor
CursHandle busyCursor;
This is a handle to the watch cursor (not animated) that is displayed (by
CursorBusy) when your dragon is processing documents in the foreground.
Note: Cursor animation is planned for a later release of Dragonsmith.
curDirDepth
short curDirDepth;
Don’t change the value of this instance variable
The depth at which your dragon is currently processing documents. A depth
of 0 indicates that the current document was specified in an Open Documents
event your dragon received. A depth of -1 indicates that the current
document is located immediately inside a folder or disk specified in an Open
Documents event.
curDocFSS
FSSpec *curDocFSS;
Don’t change the value of this instance variable; you can change the data to
which it points
Pointer to an FSSpec designating the current document. The data in
*curDocFSS is meaningful only while your dragon is processing documents.
curDocPB
PBRecUnion *curDocPB;
Don’t change the value of this instance variable; you can change the data to
which it points
This is a pointer to a PBRecUnion designating the current document. A
PBRecUnion is a union of the six parameter blocks used in making low-level
File Manager calls. The data in *curDocPB is meaningful only while your
dragon is processing documents. Your dragon is free to change, in the
ProcessFile and ProcessDirectory methods, any of the data to which curDocPB
points. If it does so, it can call SaveDocInfo to make the changes
permanent and (optionally) refresh the Finder’s display of the window
containing the document in order to make the changes visible to the user.
If you’re unfamiliar with the low-level File Manager routines and their
various parameter blocks, you can safely ignore curDocPB altogether — or you
can use the macros defined in Dragon.h and described in the section Macros
to access fields in curDocPB without having to understand how the macros
work.
Warning: Use of these macros can be tricky. Make sure that you understand
when it is and is not safe to use them.
cursorRgn
RgnHandle cursorRgn;
This instance variable is used as a parameter to WaitNextEvent. The Dragon
class sets it to NULL. Your dragon should allocate a region in cursorRgn if
it uses mouse-moved events.
dirDepthLimit
short dirDepthLimit;
Will be set in ReadPrefs
The depth (a negative value) to which your dragon will look inside nested
directories in search of documents to process. The depth of each of the
documents drag-and-dropped on your dragon is 0, so if dirDepthLimit is 0,
then your dragon will only see the documents specified in the Open Documents
event.
Warning: Recursive (but not infinitely recursive) directory scanning may
occur if dirDepthLimit < 0 and resolveAliases == TRUE. For more
information, read the section Automatic disk and folder opening above. Even
if resolveAliases == FALSE, because of the danger of stack overflow your
dragon should not set dirDepthLimit lower than about –50. (Every recursion
of ProcessDocsInDirectory requires 70 bytes on the stack, so this would
consume 3500 bytes.)
dragonPrefs
DragonPrefsRec **dragonPrefs;
Don’t change the value of this instance variable
The values in dragonPrefs are read from the ‘DrPr’ 128 resource (in
ReadPrefs) and are used to set the following instance variables:
filesOnly sleepValue[0..3]
resolveAliases sleepTime
followAliasChain dirDepthLimit
autoQuit
editMenu
MenuHandle editMenu;
Don’t change the value of this instance variable
This is a handle to the standard Edit menu implemented by the Dragon class.
fileMenu
MenuHandle fileMenu;
Don’t change the value of this instance variable
This is a handle to the minimal File menu (containing a single Quit item)
implemented by the Dragon class.
filesOnly
Boolean filesOnly;
Will be set in ReadPrefs
The value of filesOnly is tested only when all of the following are true:
• Your dragon has an ‘FREF’ resource for the document type ‘fold’ or ‘disk’
• Your dragon encounters a directory at the maximum depth (curDirDepth ==
dirDepthLimit)
• useCustomFilter == FALSE or the directory passes the CustomFilterDoc test
Under these circumstances, ProcessDirectory will be executed only if
filesOnly is FALSE. This instance variable should be set to TRUE if you
want to ensure that ProcessDirectory will never be called. See the How to…
section for more information on what combination of values to use to achieve
a particular drag-and-drop effect.
Note: It doesn’t make sense to add ‘FREF’ resources for ‘disk’ and ‘fold’
types, then set dirDepthLimit to 0 and filesOnly to TRUE.
followAliasChain
Boolean followAliasChain;
Will be set in ReadPrefs
This instance variable is only meaningful if resolveAliases == TRUE.
Normally, followAliasChain should be TRUE — if not, then an alias file at
the beginning of an alias chain will only be resolved one step of the way
rather than all the way to the original document.
menusInstalled
Boolean menusInstalled;
Don’t change the value of this instance variable
This instance variable will be set to TRUE as soon as your dragon’s menus
have been installed. Several Dragon methods rely on the value of
menusInstalled to avoid a crash.
numAEsPending
short numAEsPending;
Don’t change the value of this instance variable
This is the number of outstanding Apple events tracked by the aeQueue
object.
preferences
Preferences *preferences;
Don’t change the value of this instance variable
This is a reference to an instance of class Preferences, used to manage the
dragon’s preferences.
Note: If you don’t want your dragon to use a preferences file, you should
not alter (or delete) this object — set the ‘STR ’ 128 resource to "\p"
instead (and delete the ‘FREF’ for type prefsFileType). The preferences
object is used to manage preferences, not just a preferences file.
prefsFileType
OSType prefsFileType;
This is the file type of your dragon’s preferences file. The System 7
Finder supplies a generic icon for files of type ‘pref’, and that’s the
value that Dragon uses.
resolveAliases
Boolean resolveAliases;
Will be set in ReadPrefs
The value of this instance variable determines whether your dragon will
automatically resolve alias files that it encounters while processing
documents from an ‘odoc’ event. Normally, alias files will only be found
inside a drag-and-dropped directory, when dirDepthLimit < 0 — the Finder
automatically resolves alias files before putting them in the event’s list
of documents. An Open Documents event sent by any other application,
however, may contain references to alias files.
Warning: You should not set resolveAliases = TRUE (in your dragon’s
constructor or in its ‘DrPr’ 128 resource) unless either of these two
conditions is true:
• Nothing ‘bad’ will happen if your dragon processes the same file more
than once.
• Your dragon can tell if it’s already processed a file.
One way to make sure you don’t process the same file twice would be to call
TickCount in your dragon’s BeginProcessing method, keep the result in an
instance variable, and store this value in a resource (say, ‘PTix’ 128) in
every file that’s processed. Then a simple check (in CustomFilterDoc,
probably) of this resource for each file your dragon gets will ensure that
nothing gets processed twice. The only problem with this approach is that
would slow things down considerably.
Even if your dragon does not set resolveAliases = TRUE, there’s no guarantee
that it won’t process the same file twice — the user might well
drag-and-drop a file and an alias to the same file simultaneously. You
shouldn’t worry too much about this possibility, since there’s very little
you can do about it.
running
Boolean running;
Don’t change the value of this instance variable directly — call StopRunning
instead
This flag is TRUE as long as your dragon is running. It will be set to
FALSE in StopRunning to cause your dragon to quit (by ‘falling’ out of the
main event loop in Run).
runState
short runState;
Don’t change the value of this instance variable
The low two bits of this instance variable designate the current state of
your dragon. The following masks can be used to read these flags:
maskInBG 0x0001 Indicates the dragon is running in the background
maskBusy 0x0002 Indicates the dragon is processing documents
This instance variable is maintained for your dragon by the Dragon class’s
constructor and its BeginProcessing, EndProcessing, DoSuspend, and DoResume
methods.
signature
OSType signature;
Don’t change the value of this instance variable
This is your dragon’s application signature. You should assign unique
signatures to the dragons you create and register each with Apple.
sleepTime
long sleepTime;
This instance variable is used as a parameter to WaitNextEvent to indicate
the maximum number of ticks your dragon will relinquish to other processes.
Its value is maintained by the Dragon class’s constructor and its
BeginProcessing, EndProcessing, DoSuspend, DoResume, and ReadPrefs methods.
sleepValue[4]
long sleepValue[4];
Will be set in ReadPrefs
The four values in this instance variable denote the possible values for
sleepTime when indexed by the low two bits of runState.
useCustomFilter
Boolean useCustomFilter;
Set useCustomFilter to TRUE if your dragon’s CustomFilterDoc method is used
to filter out unwanted documents.
------------------------------------------------------
DRAGON METHODS
------------------------------------------------------
Each method description is preceded by its declaration (from the body of the
Dragon class declaration in Dragon.h). Many of the methods have
restrictions on how a subclass should or should not override them. ‘Do not
override’ should not be taken as an absolute; it simply means that you
should have a thorough understanding of what the method does in Dragon (and
why) before you override it. There are a few more explicit warnings as well
— these should be taken a bit more seriously.
Abort
virtual void Abort (short errNum);
Abort is called whenever a serious error occurs that prevents execution of
the dragon application from continuing. This method displays an error alert
terminates the program.
Warning: Your dragon should be very careful about calling Abort — in
general, you should avoid calling it any time after the Start method has
completed execution.
AdjustMenusBusy
virtual void AdjustMenusBusy (void);
Call inherited::AdjustMenusBusy if you override this method
This method disables any menu items that the user must not be able to select
while the dragon is processing documents — notably, Quit. It does not
disable the Apple menu. If your dragon has any additional menus (or menu
items), you should override this method to avoid leaving any dangerous items
enabled while the dragon is “busy.”
AdjustMenusIdle
virtual void AdjustMenusIdle (void);
Call inherited::AdjustMenusIdle if you override this method
This method is the converse of AdjustMenusBusy — it restores any menu items
that were disabled when the dragon began processing documents.
BeginProcessing
virtual void BeginProcessing (void);
Call inherited::BeginProcessing if you override this method
BeginProcessing is called by ProcessDroppings before any drag-and-dropped
documents are processed. Its default behavior in Dragon is to disable any
menu items that must not be called while processing is going on, then put up
a “busy” cursor.
CallMoreMasters
virtual void CallMoreMasters (void);
CallMoreMasters calls MoreMasters the number of times specified in the
‘MoMa’ 128 resource (as described below) in order to allocate additional
master pointers. The resource files provided in the folder ‘Template Files’
all contain a value of 0 in the ‘MoMa’ 128 resource.
CanProcessDoc
virtual Boolean CanProcessDoc (void);
Don’t override
CanProcessDoc checks to make sure that the file or directory specified in
curDocFSS and curDocPB is of a type that could be drag-and-dropped on your
dragon and applies any special filtering you’ve specified with
useCustomFilter and CustomFilterDoc.
CursorBusy
virtual void CursorBusy (void);
This method changes the cursor to a watch (not animated) to show that the
application is doing something.
Note: Cursor animation is planned for a later release of Dragonsmith.
CursorIdle
virtual void CursorIdle (void);
This method changes the cursor to show that the application is idle.
CustomFilterDoc
virtual Boolean CustomFilterDoc (void);
Override if your dragon needs to perform any additional filtering of
documents beyond that provided by the Dragon class
The CustomFilterDoc method will be called by ProcessDoc if and only if
useCustomFilter != FALSE. For example, if your dragon reads a C source file
and produces a header file with prototypes for the functions and methods
contained in it, then your CustomFilterDoc method will presumably filter out
any file whose type is not ‘TEXT’ or whose name doesn’t end with “.c”.
Note: Your CustomFilterDoc method can go ahead and call one of a number of
different document-handling methods, based on whatever criteria it chooses,
and then return FALSE to fool ProcessDoc into thinking that the document
shouldn’t be processed at all. If your dragon does this, however, you
should keep in mind that future versions of Dragonsmith may provide an
else-clause in ProcessDoc that takes some action if (it believes that) a
document could not be handled.
DoAbout
virtual void DoAbout (void);
Override this method if you want your dragon to display a splash window.
This method is executed when the user chooses the ‘About…’ item from the
Apple menu. The default in the Dragon class is to do nothing.
DoActivate
virtual void DoActivate (EventRecord *theEvent);
Override this method if your dragon has windows
This method is called by DoEvent whenever an activate event occurs (when a
window is being activated, not deactivated). Refer to Inside Macintosh
Volume VI, pages 5–14 to 5–17 for a description of the ‘SIZE’ resource’s
effect on activate events.
DoAppleMenu
virtual void DoAppleMenu (short itemNum);
Do not override
DoAppleMenu is called whenever the user chooses an item in the Apple menu.
DoBusy
virtual void DoBusy (void);
You shouldn’t have to override this method (since you can override
ShowProgress instead); call inherited::DoBusy if you do
This method is the “busy” counterpart of DoIdle — it’s called when your
dragon is processing docs rather than when it has nothing to do. The Dragon
class calls it (at the beginning of ProcessDoc) for each document to be
processed; your dragon can make additional calls to DoBusy in order to yield
CPU time to other processes and to let the user switch to another
application or cancel the document processing. The Dragon class’s DoBusy
method calls ShowProgress and WaitNextEvent (in that order).
Warning: Your dragon should not call DoBusy at any time when cancellation by
the user, or a Quit Application event sent by another process, would cause
problems. (It’s up to you to determine what ‘problems’ means…)
DoDeactivate
virtual void DoActivate (EventRecord *theEvent);
Override this method if your dragon has windows
This method is called by DoEvent whenever an activate event occurs (when a
window is being deactivated, not activated). Refer to Inside Macintosh
Volume VI, pages 5–14 to 5–17 for a description of the ‘SIZE’ resource’s
effect on activate events.
DoDiskInsert
virtual void DoDiskInsert (EventRecord *theEvent);
Do not override
This method is called by DoEvent whenever a disk insert event occurs.
Dragon::DoDiskInsert handles the event in the usual way, calling DIBadMount
to give the user a chance to initialize the disk if it’s damaged or not yet
initialized.
DoEditMenu
virtual void DoEditMenu (short itemNum);
Override this method if you add any items to your dragon’s File menu. Call
inherited::DoEditMenu if the item chosen isn’t one of the added items.
This method is executed whenever the user selects an item in the Edit menu.
Dragon::DoEditMenu implements the standard behavior of an Edit menu with
Undo, Cut, Copy, Paste, and Clear items (plus a dividing line between Undo
and Cut).
DoEvent
virtual void DoEvent (EventRecord *event);
Do not override
This is the main “switchboard” from which event handling is dispatched.
Your dragon would have to override this method only if it wanted to treat
keyDown and autoKey events differently.
DoFileMenu
virtual void DoFileMenu (short itemNum);
Override this method if you add any items to your dragon’s File menu. Call
inherited::DoFileMenu if the item chosen isn’t one of the added items.
This method is executed whenever the user selects an item in the File menu.
The default behavior in Dragon::DoFileMenu simply checks to see if the item
chosen is the last item in the menu, and calls StopRunning if it is.
Warning: If your File menu doesn’t end with Quit, then you should override
this method and not call inherited::DoFileMenu.
DoHighLevelEvent
virtual void DoHighLevelEvent (EventRecord *theEvent);
Do not override
This method is called by DoEvent whenever a high level event occurs. It
calls AEProcessAppleEvent if it’s an Apple event, or DoOtherHLEvent if it’s
not.
DoIdle
virtual void DoIdle (void);
Override this method if your dragon does some periodic task(s) while nothing
else is happening.
DoIdle is called by Run each time WaitNextEvent returns no event (or a null
event). It’s the “idle” counterpart of DoBusy, described above.
Note: When overriding this method, keep in mind that your DoIdle will be
called only when your dragon is not processing any documents and no
already-received Apple events are waiting to be processed. Your method can
rely on these facts to make assumptions about the dragon’s state.
DoKeyDown
virtual void DoKeyDown (EventRecord *theEvent);
Call inherited::DoKeyDown if you override this method and your method
doesn’t handle the event
This method is called by DoEvent whenever a keyDown or autoKey event occurs.
It checks for command–key combinations (those in the menus as well as
–period).
DoMenu
virtual void DoMenu (long menuItemCode);
Override this method if you add any menus to your dragon. Call
inherited::DoMenu if the item chosen isn’t in one of the added menus.
DoMenu is called in response to a mouseDown in the menu bar or command-key
menu equivalent.
DoMouseDown
virtual void DoMouseDown (EventRecord *theEvent);
Override this method if your dragon has windows
This method is called by DoEvent whenever a mouseDown event occurs.
DoMouseUp
virtual void DoMouseUp (EventRecord *theEvent);
This method is called by DoEvent whenever a mouseUp event occurs. You may
need to override this method if you override DoMouseDown.
DoOapp
virtual OSErr DoOapp (AppleEvent *theAppleEvent, AppleEvent *theReply,
long refcon);
This method is called when the user launches the dragon from the Finder by a
means other than drag-and-drop (i.e., by double-click, select and open, or
select and Cmd–Down Arrow). The default DoOapp method in Dragon just checks
to see if autoQuit == TRUE and calls StopRunning if it is.
DoOdoc
virtual OSErr DoOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply,
long refcon);
DoOdoc is called by the Apple event handler function HandleOdoc whenever the
dragon receives an Open Documents (‘odoc’) event. If the dragon is already
processing an Apple event, it calls SuspendAEvent and returns. Otherwise,
it gives the list of documents in the keyDirectObject parameter of the Open
Documents event to ProcessDropping. Finally, it checks to see if autoQuit
== TRUE and calls StopRunning if it is.
DoOSEvent
virtual void DoOSEvent (EventRecord *theEvent);
Override this method if your dragon handles mouse-moved events or maintains
its own private scrap
This method is called by DoEvent whenever an OS event occurs. It in turn
calls DoSuspend or Doresume.
DoOtherHLEvent
virtual void DoOtherHLEvent (EventRecord *theEvent);
Override this method if your dragon handles non-Apple event high level
events
This method is called by DoHighLevelEvent whenever a high level event that
is not an Apple event occurs.
DoPdoc
virtual OSErr DoPdoc (AppleEvent *theAppleEvent, AppleEvent *theReply,
long refcon);
DoPdoc will be called if the user (in the Finder) selects a file that
belongs to your dragon and chooses “Print” from the File menu. Very few
dragons will need to override this method — the default Dragon::DoPdoc
merely returns an error. Like DoOapp and DoOdoc, it checks to see if
autoQuit == TRUE and calls StopRunning if it is.
DoQuit
virtual OSErr DoQuit (AppleEvent *theAppleEvent, AppleEvent *theReply,
long refcon);
DoQuit calls StopRunning, which will set running = FALSE to terminate the
main event loop.
DoResume
virtual void DoResume (void);
Override this method if your dragon needs to perform any additional actions
when it moves to the background.
DoResume is called whenever your dragon receives a resume event.
Dragon::DoResume adjusts the values of runState and sleepTime and, if the
dragon is busy processing documents, calls CursorBusy to show a busy cursor.
DoSuspend
virtual void DoSuspend (void);
Override this method and call inherited::DoSuspend if your dragon needs to
perform any additional actions when it moves to the background.
DoSuspend is called whenever your dragon receives a suspend event.
Dragon::DoSuspend adjusts the values of runState and sleepTime and, if the
dragon is busy processing documents, calls CursorIdle to restore the
standard arrow cursor.
DoUpdateEvent
virtual void DoUpdateEvent (EventRecord *theEvent);
Override this method if your dragon has windows
This method is called by DoEvent whenever an update event occurs.
Dragon
Dragon (void);
This is the Dragon constructor, which is called when the gDragon object is
created (in main), before the constructor (if there is one) for your
subclass of Dragon.
EndProcessing
virtual void EndProcessing (void);
Call inherited::EndProcessing if you override this method
This method performs “housekeeping” tasks associated with the completion of
document processing — it sets sleepTime to the appropriate value and calls
AdjustMenusIdle and CursorIdle.
Error
virtual void Error (short errNum);
The Error method displays a very simple alert showing a brief message and
the number passed in the errNum parameter.
FindPrefsFile
virtual Boolean FindPrefsFile (FSSpec *fss);
Do not override
This method looks for your dragon’s preferences file (the name of which is
specified in your dragon’s ‘STR ‘ 128 resource). It looks first in the same
folder that contains your dragon, then in the Preferences folder within the
System folder. If necessary, it calls MakePrefsFile to create a new file.
Finish
virtual void Finish (void);
Call inherited::Finish if you override this method
Finish is called by main after the Run method has ended. It’s responsible
for any last-minute “clean-up” — close any open files, delete temporary
files, remove trap patches, dispose of objects, etc. The default behavior
in Dragon is to dispose of the preferences object, flush the Apple events
queue (returning errors in the reply events of any pending Apple events),
and dispose of the aeQueue object.
Warning: If you override Finish, you must call inherited::Finish at the end
of your method. If the code in Dragon::Finish is not executed, the current
preferences will not be saved and there is a possibility (of unknown
likelihood) that a crash will occur if there are any pending Apple events.
FlushAEventQueue
virtual void FlushAEventQueue (void);
Do not override
FlushAEventQueue is called by Finish when there any pending Apple events.
It asks the aeQueue object for each of the pending Apple events, then calls
AEResumeTheCurrentEvent with a pointer to the ReturnEventNotHandled
function. This tells the sender of each pending Apple event that the event
wasn’t handled — since Finish is the last code that is executed, there’s no
possibility of handling any events at this point.
InitAppleEvents
virtual void InitAppleEvents (void);
Override this method if your dragon handles any additional Apple events
InitAppleEvents installs handlers for the four required Apple events, and
calls MakeAEQObject.
InitMac
virtual void InitMac (void);
Override this method and call inherited::InitMac (at the beginning of your
method) if your dragon requires more than the usual initializations.
InitMac initializes the usual system software managers. Dragon::InitMac
calls InitGraf, InitFonts, InitWindows, InitMenus, TEInit, InitDialogs, and
InitCursor (in that order).
InitMem
virtual void InitMem (void);
Override this method if you want to increase the amount of memory allotted
to your dragon’s stack
InitMem enlarges your dragon’s application zone to its maximum size and
calls CallMoreMasters.
InitMilieu
virtual void InitMilieu (void);
Do not override this method — add to the ‘GChk’ resource instead
InitMilieu checks your dragon’s run-time environment against the
specifications in the ‘GChk’ 128 resource (as described below), and calls
Abort with an appropriate error code if the run-time environment is not
sufficient to run your dragon. Otherwise, it calls InitAppleEvents.
InitPrefs
virtual void InitPrefs (void);
Do not override
This method creates and initializes the preferences object, asks
FindPrefsFile to look for a preferences file, and (if one is found or
created) tells the preferences object to use that file. If the preferences
file had to be created, then the appropriate preferences resources will be
copied to it from the dragon’s application file. InitPrefs ends by calling
ReadPrefs to read in the preferences from the file’s resources.
InteractWithUser
virtual Boolean InteractWithUser (long timeOut);
Do not override
Your dragon should call this method if it needs to interact with the user
(by putting up a dialog, for example) while it’s processing documents.
InteractWithUser calls the Toolbox routine AEInteractWithUser using an idle
function (CallWaitIdle, described below) that ensures that events will be
processed normally while idling.
The timeOut parameter is the number of ticks your dragon is willing to wait
for the user to bring the dragon to the foreground. A reasonable value for
timeOut might fall between 600 and 7200 ticks (10 seconds and 2 minutes).
IsPrefsFile
virtual Boolean IsPrefsFile (FSSpec *fss);
Do not override
This method checks the file specified by fss to make sure it has the proper
creator and type to be your dragon’s preferences file.
Note: IsPrefsFile does not test the validity of the file’s resources — that
responsibility falls to the VerifyResources method of your subclass of the
Preferences class, if you choose to make one.
MakeAEQObject
virtual AppleEventQueue *MakeAEQObject (void);
Override this method if you implement a subclass of AppleEventsQueue
MakeAEQObject creates, and returns a reference to, an instance of class
AppleEventsQueue(or of a subclass of AppleEventsQueue, if you override this
method).
MakePrefsFile
virtual Boolean MakePrefsFile (FSSpec *fss);
Do not override
This method creates a new preferences file, as specified by fss. It does
not add any resources to the file.
MakePrefsObject
virtual Preferences *MakePrefsObject (void);
Override this method if you implement a subclass of Preferences
MakePrefsObject creates, and returns a reference to, an instance of class
Preferences (or of a subclass of Preferences, if you override this method).
ProcessDirectory
virtual void ProcessDirectory (void);
Your dragon should override this method if it processes directories (folders
and volumes) themselves rather than their contents (files and other
directories).
ProcessDirectory will be executed only when —
( filesOnly == FALSE
&& curDocIsDirectory // it's a folder or disk
&& curDirDepth == dirDepthLimit
&& CanProcessDoc () )
Here are some examples of the functionality of dragons that would need to
override ProcessDirectory:
• Show information about a disk [presumably more that what the Finder’s
“Get Info” gives you]
• Scan a folder (or folders) for new files dropped in it (or them), then
call ProcessFile for each such file (for example, to monitor incoming mail
or expand files [this is essentially what DownLine does]
• Copy the full paths of selected directories (and files) to the clipboard
[this is what FilePaths does]
ProcessDoc
virtual void ProcessDoc (void);
Don’t override
This method (which should normally not be overridden) is the “bottleneck”
through which all documents must pass before they can be processed. It
begins by checking to see that the document specified by curDocFSS and
curDocPB (both of which are valid at this point) is of a kind that could be
drag-and-dropped on your dragon, then applies any special filtering that you
may have set up in CustomFilterDoc. If the document meets those
requirements, ProcessDoc passes it as follows:
• If the document is a file belonging to your dragon (curDocIsFile &&
curDocCreator == this->signature), then it’s passed to ProcessOwnedFile.
• If the document is a file that does not belong to your dragon, then it’s
passed to ProcessFile — this is the usual case.
• If the document is a folder or volume and the maximum level of folder
opening (see the descriptions of curDirDepthLimit and dirDepthLimit in the
Instance Variables section) has not yet been reached, then it’s passed to
ProcessDocsInDirectory, which will process the documents inside the folder
(or volume).
• If the document is a folder or volume at the maximum level of folder
opening, and if your dragon sets filesOnly = FALSE, then it will be passed
to ProcessDirectory.
ProcessDocsInDirectory
virtual void ProcessDocsInDirectory (short vRefNum, long dirID);
Don’t override
This method looks through the current directory and, for each thing it finds
there, sets up curDocFSS and curDocPB and calls ProcessDoc.
ProcessDroppings
virtual OSErr ProcessDroppings (AEDescList *docList);
ProcessDroppings loops through the documents that were drag-and-dropped on
your application and calls ProcessDoc for each one after setting up
curDocFSS and curDocPB. Alias files are resolved here (if resolveAliases !=
FALSE). ProcessDroppings calls BeginProcessing before handling the first
document and EndProcessing after the last.
ProcessFile
virtual void ProcessFile (void);
Override except in unusual cases
ProcessFile is the heart of any typical sub-class of Dragon. Unless your
dragon only handles volumes or folders, you will certainly need to override
this method — this is where the actual work is done (or the methods that do
the work are called).
ProcessOwnedFile
virtual void ProcessOwnedFile (void);
Override if your dragon creates any special file types (other than
preferences files) and treats them differently than it does files which it
didn’t create
The default behavior for ProcessOwnedFile is to check if the file designated
by curDocFSS and curDocPB is a preferences file and, if it is, to use the
preferences found in it.
ReadPrefs
virtual void ReadPrefs (void);
Call inherited::ReadPrefs if you override this method
The purpose of this method is to read in settings from the dragon’s
preferences file (from the resources specified in its ‘PrRo’ 128 resource —
see description below) and change the appropriate instance variables
accordingly. It should call preferences->GetPrefResource for each
preferences resource. The Dragon::ReadPrefs method sets the following
instance variables:
filesOnly sleepValue[0..3] resolveAliases sleepTime
followAliasChain dirDepthLimit autoQuit
You should check to see if preferences->GetPrefResource returns NULL when
overriding this method, and in any other method that might rely on a
preferences resource being in memory — an incomplete preferences file should
never cause a crash. See the code in the How do I… section above for an
example.
Note: Remember that your dragon’s preferences will come from the application
file if no preferences file could be found or created.
ResumeAEvent
virtual void ResumeAEvent (void);
Do not override
This method is called by Run whenever WaitNextEvent doesn’t report an event
and one or more previously received Apple events are pending. ResumeAEvent
asks the aeQueue object for the oldest pending Apple event, then calls
AEResumeTheCurrentEvent to handle the event normally.
Run
virtual void Run (void);
Do not override
Run contains the main event loop, which calls either DoEvent, DoIdle, or (if
there are any pending Apple events) ResumeAEvent. The loop terminates only
when this->running is set to FALSE (by StopRunning).
SaveDocInfo
virtual void SaveDocInfo (Boolean refreshFinder);
Call SaveDocInfo right after you change one or more of the following values:
curFileType curFileCreator curFileFlags
curDocCreated curDocModified
or any other field in *curDocPB that PBSetCatInfo takes as input.
Note: To rename the current document, you should call FSpRename (rather than
just changing *curDocName) after calling SaveDocInfo (TRUE).
Warning: If your dragon makes any low-level File Manager calls that leave
garbage in any fields in *curDocPB that PBSetCatInfo takes as input, then it
should do so after calling this method, or save and restore those values, or
refrain from calling SaveDocInfo at all. Table 2 contains a partial list of
low-level File Manager calls that leave garbage in fields that PBSetCatInfo
uses.
<Table 2 omitted>
SetUpMenus
virtual void SetUpMenus (void);
If you override this method, call inherited::SetUpMenus at the beginning of
your method and DrawMenuBar at the end
SetUpMenus is called by Start (unless autoQuit != FALSE). It initializes
your dragon’s menus, including (by default) a standard Apple Menu, a File
menu with just one item (Quit), and an Edit menu with the five basic editing
commands (Undo, Cut, Copy, Paste, and Clear).
ShowProgress
virtual void ShowProgress (void);
For future compatibility, you should call inherited::DoBusy if you override
this method
Override ShowProgress if you want your dragon to give the user some visual
indication of the progress it’s making on processing the documents the user
drag-and-dropped on it. If your dragon doesn’t look inside directories
(i.e., if dirDepthLimit == 0) then you might want to show a progress bar
similar to the one the Finder puts up while copying files. If it does look
in directories, then you could display the name of each document as it’s
processed.
Warning: ShowProgress is called for every file, folder, or disk
drag-and-dropped on your dragon (or sent in an Open Documents event from a
source other than the Finder) — even ones that your dragon can’t (for
whatever reason) process. Keep this mind if you want to display the names
only of documents as they’re processed — you’ll need to override
ProcessFile, ProcessDirectory, or ProcessDocsInDirectory (or some
combination of those three) instead.
Also, your DoBusy method should not change *curDocFSS or *curDocPB in any
way.
Note: Implementing a movable modal window (as an instance of a class that
you can override) is high on the list of future plans for Dragonsmith, but
adding it to Dragon will probably require significant changes to ProcessDoc
and ShowProgress. If you override either of these methods, keep in mind
that your code may have to be changed later for compatibility with new
releases of Dragonsmith.
Start
virtual void Start (void);
Start initializes your dragon’s run-time environment — memory, menus,
preferences, events, etc.
StopProcessing
virtual void StopProcessing (OSErr err);
Call inherited::StopProcessing if you override this method
StopProcessing should be called whenever an error occurs that is serious
enough that processing should be stopped (including cancellation by the
user). The parameter should specify the reason for stopping.
Dragon::StopProcessing just sets abortProcessing = TRUE; your Dragon
subclass can display an error message or otherwise indicate that something
went wrong (but it should stop “quietly” if the user generated a cancel
event).
StopRunning
virtual void StopRunning (void);
This method sets running = FALSE, after calling StopProcessing if your
dragon is currently processing an Open Documents event. The dragon’s main
event loop will terminate as soon as control is returned to Run.
SuspendAEvent
virtual OSErr SuspendAEvent (AppleEvent *event, AppleEvent *reply,
AEHandlerFunc *handler, long refcon);
Do not override
SuspendAEvent is called by ProcessOdoc any time your dragon receives an Open
Documents event while processing a previously received one. SuspendAEvent
asks the aeQueue object to add the event to its queue, then calls the
Toolbox routine AESuspendTheCurrentEvent to let the System know the event
isn’t being handled.
Warning: Be very careful if you decide to override this method. Any
application that receives an Apple event while processing one received
earlier must either call AESuspendTheCurrentEvent or interrupt the first
event until handling of the second is completed. It appears that dividing
CPU time between the handling of several Apple events (without calling
AESuspendTheCurrentEvent and AEResumeTheCurrentEvent at the appropriate
times) will cause the system to grow very confused!
WaitIdle
virtual Boolean WaitIdle (EventRecord *theEvent, long *sleep,
RgnHandle *mouseRgn);
WaitIdle will be called whenever your dragon gets an update, activate, null,
or OS event while it’s waiting for AEInteractWithUser to return — i.e.,
while it waits for the user to bring it to the foreground. The default
behavior in the Dragon class is to let the main event handler, DoEvent, pass
the buck. Please note that recursion is not a possibility unless you do
some very strange overriding of the DoActivate, DoUpdateEvent, DoOSEvent,
DoSuspend, or DoResume methods.
------------------------------------------------------
RESOURCES
------------------------------------------------------
This section briefly describes all of the resources that should be present
in your dragon’s project resource file (with the exception of the ‘SIZE’ and
signature resources, which THINK C will add to the built application). All
of the project resource files provided with Dragonsmith 1.1 contain these
resources. The best way to gain a full understanding of the
Dragonsmith-specific resources is to view them in ResEdit and look for
mentions of them in the Dragonsmith source code.
Dragonsmith resources
The following resources have Dragonsmith-specific functions:
ALRT 129 Alert used by Dragon::Error.
DITL 129 Dialog item list for use with ‘ALRT’ 129.
DrPr 128 Dragon class preferences. The following Dragon instance variables
are all copied from values in this resource:
autoQuit resolveAliases followAliasChain
sleepValue[0..3] filesOnly dirDepthLim
If your dragon sets any of these instance variables in its constructor, then
you should put the same values into the fields in your project resource
file’s ‘DrPr’ 128 resource. Forgetting to do so can cause tremendous
frustration.
GChk 128 A list of Gestalt calls and the values that they should return. InitMilieu
passes this resource to the GestaltBatchCheck function, which returns noErr if the
run-time environment is sufficient for the dragon to function properly. Each entry
in the list has the following structure:
typedef struct {
OSType selector; // Selector to call Gestalt with
short compOp; // Type of comparison to make
short failureError; // Error to return if check fails
long compValue; // Check Gestalt result against this
}
Possible values for the compOp field are listed in the file GestaltUtils.h.
MENU 128 Apple menu,
129 File menu,
130 Edit menu.
MoMa 128 Number of times to call MoreMasters.
PrRo 128 Preferences roster — a list of the dragon’s preferences resources.
You must add to this list whenever you add a new preferences resource.
STR 128 Name of the dragon’s preferences file. To prevent a preferences
file from being created, put a null string in this resource and delete the
‘FREF’ resource for the type designated by the instance variable
prefsFileType.
ResEdit templates for the Dragonsmith-specific resources are available in
the ‘Dragonsmith TMPLs’ file — keep this file open whenever viewing or
editing them.
Finder-related resources
The following resoures are used by the Finder to correctly launch an
application, control drag-and-drop onto it, and display its icons and
information in the Finder:
<signature> 0 The type of this resource should be the same as your
application’s signature (set via the ‘Set Project Type…’ dialog in THINK C).
It contains a copyright string for the application.
BNDL 128 This resource coordinates an application’s Finder-related
resources. You can ResEdit 2.1’s ‘BNDL’ editor to modify this resource as
well as the ‘FREF’, signature, and icon resources.
FREF 128, etc. These resources designate the types of files your dragon can
process. Make sure you have one for every document type your dragon is
capable of processing, plus one (‘APPL’) for the application itself, plus
one for the file type designated by the instance variable prefsFileType
(normally ‘pref’) if your dragon creates a preferences file.
Use the values ‘fold’ and ‘disk’ to allow drag-and-drop of folders and
volumes (respectively) onto your dragon.
Use the value ‘****’ to specify that your dragon can process files (not
folders or volumes) of any type.
hfdr
styl
TEXT 128 These three resources in combination provide Ballon Help for your
dragon’s icon in the Finder. Delete all three if you don’t want this
feature.
icl4,
icl8,
ICN#,
ics#,
ics4,
ics8 Icons for your dragon’s application and files.
SIZE –1 The SIZE resource must be set up correctly for an application to
accept Apple events, and the Dragon class relies on Apple events — if a
dragon can’t get an Open Documents event, it won’t do anything. Don’t put
your own ‘SIZE’ resource in the .rsrc file — use the SIZE settings available
by choosing ‘Set Project Type…’ from THINK C’s Project menu.
vers 1 This is the ‘true’ version string which appears in the Get Info…
window
vers 2 This string appears directly under your application’s name in its Get
Info… window in the Finder. The dragons provided with Dragonsmith use the
words ‘a Dragon sub-species’ in this string, but you’re not required to
follow this convention in your own dragons.
Inside Macintosh, Volume VI has an extensive description of Finder-related
resources on pages 9-5 to 9-24 and 9-34 to 9-35 — read it for more
information on these resources. Keep in mind that many of these resources
must be modified before your dragon’s first built version, or you may have
to rebuild the desktop to make the Finder’s behavior reflect any changes in
them.